//
// System.Runtime.Serialization.ObjectIDGenerator.cs
//
// Author: Duncan Mak  (duncan@ximian.com)
//         Lluis Sanchez (lsg@ctv.es)
//
// (C) Ximian, Inc.
// Copyright (C) 2004,2006 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#ifndef ObjectIDGenerator_h
#define ObjectIDGenerator_h

#include <System/Object.h>
#include <System/Runtime/Serialization/ISurrogateSelector.h>
#include <System/Runtime/Serialization/StreamingContext.h>
#include <System/Collections/IHashCodeProvider.h>
#include <System/Collections/Hashtable.h>
#include <System/Collections/IComparer.h>
#include <System/Collections/IHashCodeProvider.h>
#include <System/ArgumentNullException.h>
#include <System/Int64.h>

namespace Riccsson
{
	namespace System
	{
		namespace Runtime
		{
			namespace Serialization
			{
				class ObjectIDGenerator
				{
					PROP3GET(ObjectIDGenerator, long, NextId);

					// ObjectIDGenerator must generate a new id for each object instance.
					// If two objects have the same state (i.e. the method Equals() returns true),
					// each one should have a different id.
					// Thus, the object instance cannot be directly used as key of the hashtable.
					// InstanceComparer compares object references instead of object content
					// (unless the object is inmutable, like strings).

					private: class InstanceComparer: public Riccsson::System::Collections::IComparer, public Riccsson::System::Collections::IHashCodeProvider
					{
						public: int /*IComparer.*/Compare (object* o1, object* o2)
						{
							if (instanceof(o1, Riccsson::System::string))
								return o1->Equals(o2) ? 0 : 1;
							else 
								return (o1 == o2) ? 0 : 1;
						}

						public: int /*IHashCodeProvider.*/GetHashCode (object* o)
						{
							//return object::InternalGetHashCode (o);
							return o->GetHashCode();
						}
					};
		
					// Private field
					GC_PTR<Riccsson::System::Collections::Hashtable> table;
					Int64 current; // this is the current ID, starts at 1
					static GC_PTR<InstanceComparer> comparer;

					STATIC(ObjectIDGenerator)
					{
						comparer = new InstanceComparer ();
					}

					// constructor
					public: ObjectIDGenerator ()
						: PROP3GET_INIT(ObjectIDGenerator, NextId)
					{
						table = new Riccsson::System::Collections::Hashtable (comparer, comparer);
					}

					// Methods
					public: virtual long GetId (object* obj, bool out firstTime)
					{
						if (obj == null)
							throw ArgumentNullException ("obj");

						object* val = table [obj];

						if (val != null) {
							firstTime = false;
							return (long) val;

						} else {
							firstTime = true;
							table->Add (obj, &current);
							return current++; 
						}
					}

					public: virtual long HasId (object* obj, bool out firstTime)
					{
						if (obj == null)
							throw ArgumentNullException ("obj");

 						object* val = table [obj];
 
 						if (val != null) {
 							firstTime = false;
 							return (long) val;

						} else {				
							firstTime = true;
							return 0L; // 0 is the null ID
						}
					}

					/*internal*/ public: PROP3_GET(long, NextId)
					{
						return current ++;
					}
				};
			}
		}
	}
}

#endif